#include <string>
#include <iostream>
#include <sstream>


#ifdef USE_LOADGEN_GUI

#else
    #include <boost/utility.hpp>
    #include <boost/thread/thread.hpp>
    #include <boost/thread/xtime.hpp>
#endif // USE_LOADGEN_GUI

#include "SingleCall.h"
#include "eventhistory.h"

using std::string;
using std::cout;
using std::endl;
using std::cerr;
using std::ostringstream;
using std::vector;

void* (*pt2Object);

#ifdef USE_LOADGEN_GUI
#  define SLEEP(milliseconds) QThread::msleep(milliseconds);
#else
    void SLEEP(int miliseconds) {
        boost::xtime xt; 
        boost::xtime_get(&xt, boost::TIME_UTC); 
        xt.sec += miliseconds/1000;
        xt.nsec += ((miliseconds%1000)*1000000); 
        boost::thread::sleep(xt);
    }
 #endif // USE_LOADGEN_GUI


uint SingleCall::_numberOfSingleCalls = 0;
uint SingleCall::_numberOfStartedCalls = 0;
uint SingleCall::badEndedCalls = 0;
uint SingleCall::rtpPort = MIN_RTP_PORT;

#ifdef USE_LOADGEN_GUI
    QMutex SingleCall::_mutex;
    QMutex SingleCall::_cbMutex;
#else
    boost::mutex SingleCall::_mutex;
    boost::mutex SingleCall::_cbMutex;
#endif // USE_LOADGEN_GUI


SingleCall::SingleCall(vector<string> data,
                       int repeatCount,
                       string filename):_filename(filename)
{
    if(rtpPort > MAX_RTP_PORT)
    {
        std::cerr<<"Out of portunmbers and portnumber resetted"<<std::endl;
        rtpPort = MIN_RTP_PORT;
    }
    currentRtpPort = rtpPort;
    rtpPort +=2;
    _data = data;
    _repeatCount = repeatCount;
    _currentRepeat = 0;
    _myNumber    = _numberOfSingleCalls++;
    _playingFile = false;
     _doForceHangup=false;
    _state = STATE_LOADED;
    _eventHistory = new EventHistory();
}

SingleCall::~SingleCall()
{
    delete _eventHistory;
}

int SingleCall::getMyNumber()
{
    return _myNumber;
}
void SingleCall::resetNumberOfSingleCalls()
{
    SingleCall::_numberOfSingleCalls=0;
}

void SingleCall::addEventHistoryItem(string event)
{
    //#ifndef USE_LOADGEN_GUI
    //cout << " " << _myNumber << " \t " << event << std::endl;
    //#endif // !USE_LOADGEN_GUI
    _eventHistory->push_back(event);
    #ifdef USE_LOADGEN_GUI
    emit ( updateListOfElements ( _myNumber , false ) );  // let the GUI know he should refresh
    #endif // USE_LOADGEN_GUI
}

EventHistory* SingleCall::getEventHistory()
{
    return _eventHistory;
}

bool SingleCall::createLine(string fromIdentity)
{

    sipxLineAdd(_sipXInst, fromIdentity.c_str(), &_sipXLine) ;
    //sipxLineRegister (_sipXLine, true); //TODO 
    SIPX_RESULT result = sipxCallCreate(_sipXInst, _sipXLine, &_sipXCall);
    if (  result == SIPX_RESULT_SUCCESS ) return true;
    else std::cerr << "ERROR: sipxCallCreate " << result << std::endl;
    return false;
}

bool SingleCall::placeCall(string destinationUrl)
{
    setState(STATE_CALLING);
    bool bRC = false ;
    sipxCallConnect(_sipXCall, destinationUrl.c_str()) ;
    bRC = waitForSipXEvent(CONNECTED, 5) ;
    return bRC ;
}

bool SingleCall::playFile(string filename)
{
    if( SIPX_RESULT_SUCCESS == sipxCallPlayFileStart(_sipXCall, filename.c_str(), true, true, true) )
    {
        _playingFile = true;
        return true ;
    }
    return false ;
}

bool SingleCall::playTones(string tones)
{
    bool bRC = true ;
    for (uint i = 0 ; i < tones.size(); i++)
    {
        int toneId = tones.at(i);

        if (    (toneId >= '0' && toneId <= '9') ||
                (toneId == '#') || (toneId == '*') || toneId == ',' || toneId == '!')
        {
            if (toneId == ',')
            {
                //cout << _myNumber <<"\t<-> Playtone: Sleeping for 2 seconds\n" ;
                SLEEP(2000) ;
            }
            else
            {
                //cout << _myNumber <<"\t<-> Playtone: " << toneId << "\n" ;
                SLEEP(250) ;
                if (sipxCallStartTone(_sipXCall, static_cast<TONE_ID> (toneId), true, true) != SIPX_RESULT_SUCCESS)
                {
                    cerr << _myNumber <<"\t<!> Playtone '" << toneId << "' returned error"<<endl;
                }
                SLEEP(500) ;
                sipxCallStopTone(_sipXCall) ;
            }
        }
        else
        {
            bRC = false ;
            break ;
        }

    }

    return bRC ;
}

// Wait for the designated event for at worst ~iTimeoutInSecs seconds
bool SingleCall::waitForSipXEvent(SIPX_CALLSTATE_MAJOR eMajor, int iTimeoutInSecs)
{
    bool bFound = false ;
    int  tries = 0;

    // TODO TODO Kids, don't try this at home -- This method of waiting for events is
    // not recommended.  Generally, most UAs are asynchronous and event driven -- 
    // if you need to want for an event, build something that is thread safe and doesn't use sleeps.
    // I probably should use a waitcondition here, we do have to adapt the eventCallBack method a little
    while (!bFound)
    {
        for (int i=0;i<MAX_RECORD_EVENTS; i++)
        {
            //cerr << _myNumber << "\t<-> waitForSipXEvent : " << g_eRecordEvents[i] <<endl;
            if (g_eRecordEvents[i] == eMajor)
            {
                bFound = true ;
                break ;
            }
        }

        if (!bFound)
        {
            SLEEP(1000) ;
            if (++tries > (iTimeoutInSecs))
            {
                break ;
            }
        }
    }

    return bFound ;
}

bool SingleCall::isHangedUp()
{
    if (_state >= STATE_HANGED_UP ) return true;
    return false;
}

void SingleCall::forceHangUp()
{
    if (_state >= STATE_FORCED_HANGED_UP ) return; // don't try again if he's already trying
    if (_state < STATE_HANGED_UP ) _doForceHangup=true;
    setState(STATE_FORCED_HANGED_UP);
}

bool SingleCall::shutdownCall()
{
    setState(STATE_HANGED_UP);

    clearSipXEvents() ;
    sipxCallDestroy(_sipXCall) ;
    sipxLineRemove(_sipXLine) ;

    waitForSipXEvent(DESTROYED, 5) ;  // FIXME wait using either boost of QT threads

    return true ;
}

bool SingleCall::eventCallBack(SIPX_EVENT_CATEGORY category, 
                   void* pInfo, 
                   void* pUserData)
{
    //char cBuf[1024] ;
    //cout << sipxEventToString(category, pInfo, cBuf, sizeof(cBuf)) << endl;    

    if (category == EVENT_CATEGORY_CALLSTATE)
    {
        SIPX_CALLSTATE_INFO* pCallInfo = static_cast<SIPX_CALLSTATE_INFO*>(pInfo);
        /*cout << "    hCall=" << pCallInfo->hCall ;
        cout << ", hAssociatedCall=" << pCallInfo->hAssociatedCall << endl;

        if (pCallInfo->cause == CALLSTATE_AUDIO_START)
        {
            cout << "* Negotiated codec: " << pCallInfo->codecs.audioCodec.cName ;
            cout << ", payload type " << pCallInfo->codecs.audioCodec.iPayloadType << endl;
        }*/
        g_eRecordEvents[g_iNextEvent] = pCallInfo->event;

        if( (g_eRecordEvents[g_iNextEvent] == CALLSTATE_DISCONNECTED 
                                && _state < STATE_HANGED_UP)
                || g_eRecordEvents[g_iNextEvent] == CALLSTATE_UNKNOWN)
            {
                incrementBadEndedCalls();
                setState(STATE_STOPPED_ERROR);
            }
        g_iNextEvent = (g_iNextEvent + 1) % MAX_RECORD_EVENTS ;
    }
    return true;
}

void SingleCall::clearSipXEvents()
{
    for (int i=0;i<MAX_RECORD_EVENTS; i++)
    {
        g_eRecordEvents[i] = CALLSTATE_UNKNOWN ;
    }
    g_iNextEvent = 0 ;
}


bool SingleCall::wrapper2EventCallBack(SIPX_EVENT_CATEGORY category, 
                   void* pInfo, 
                   void* pUserData)
{
    #ifdef USE_LOADGEN_GUI
    _cbMutex.lock();
    #else
    boost::mutex::scoped_lock scoped_lock(_cbMutex); 
    #endif // USE_LOADGEN_GUI

    // explicity cast global variable <pt2Object> to a pointer to SingleCall
    // WARNING <pt2Object> _MUST_ point to an appropiate object !!!!!!
    int *convertedPUserData = (int*)(pUserData);
    SingleCall* mySelf = (SingleCall*) pt2Object[ *convertedPUserData ];
    // call member
    bool returnBool = mySelf->eventCallBack(category, pInfo, pUserData);

    #ifdef USE_LOADGEN_GUI
    _cbMutex.unlock();
    #endif // USE_LOADGEN_GUI

    return returnBool;
}

void SingleCall::operator()() 
{
    run();
}

void SingleCall::run()
{
    setState(STATE_RUNNING);
    // initialize sipx TAPI-like API
    tempLockInitializeResources();

    for (uint i=0; i<_repeatCount; i++)
    {
        clearSipXEvents() ;
        char cs[25];
        _currentRepeat = i+1;
        sprintf(cs, "Attempt %d of %d", i+1, _repeatCount);
        addEventHistoryItem( cs );
        _numberOfStartedCalls++;
        #ifdef USE_LOADGEN_GUI
        emit( updateStatistics());
        emit( updateThreadInfo( _myNumber, _currentRepeat ,QString(SingleCall::humanTranslateLoadgenState(_state).c_str()) ) );
        #endif // USE_LOADGEN_GUI
        for(uint j=0;j<_data.size();j++)
        {
            if ( STATE_FORCED_HANGED_UP == _state ) 
            {
                if (_doForceHangup) {                   // inline part to force hangup, and prevent application 
                    parseCommand("hang");               // from freezing when asking all calls to hangup
                    setState(STATE_FORCED_HANGED_UP);
                    addEventHistoryItem("<!>Forced Hangup by User");
                    _doForceHangup=false;
                }
                break;
            }
            string command =  _data.at(j);

            bool retCommand= parseCommand(command);
            if (!retCommand) break;
            //addEventHistoryItem(command); SLEEP(static_cast<int>(rand()/5000000));
        }

    }
    sipxEventListenerRemove(_sipXInst, wrapper2EventCallBack, &_myNumber) ;

    if ( _myNumber != (SingleCall::_numberOfSingleCalls-1) ) {
        tempLockUnInitializeResources();    // FIXME crashes the system at last instance, 
                                            // as a temporary fix I'll keep one alive
    }
    setState(STATE_STOPPED);
}


bool SingleCall::parseCommand(string command)
{
    addEventHistoryItem(command);
    string token1="", token2="";
    size_t pos1=0;
    string sep = " \t";

    // search for sep character and extract the substring
    if ((pos1=command.find_first_of(sep)) != string::npos )
        token1 = command.substr(0, pos1);
    if (token1.size() == 0 && command.size() != 0 ) token1 = command;
    if (pos1 < command.size()) token2 = command.substr(pos1,command.size());
    // strip of leading whitespaces and tabs of token2
    while (token2.find_first_of(sep) == 0) 
        token2 = token2.substr(1,token2.size());  

    // Read the first token to see what command it is
    if (token1.compare("from") == 0)
    {
        if (token2.size() == 0)
        {
            // this error should not be shown
            cerr << _myNumber <<"\t<!> FROM command requires Identity URL."<<endl;
            setState(STATE_STOPPED_ERROR);
            exit(1);
        }
        if(!createLine(token2))
        {
            //cerr << _myNumber <<"\t<!> Unable to create call as '" << token2 << endl;
            addEventHistoryItem("Unable to create call with the FROM address" );
            setState(STATE_STOPPED_ERROR);
            return false;
        }
    }
    else if (token1.compare("call") == 0)
    {
        if (token2.size() == 0)
        {
            //cerr << _myNumber <<"\t<!> CALL command requires Destination URL."<<endl;
            addEventHistoryItem("<!> CALL command requires Destination URL.");
            setState(STATE_STOPPED_ERROR);
            exit(1);
        }
        if(!placeCall(token2))
        {
            //cerr << _myNumber <<"\t<!> Unable to initiate call to '" << token2 << endl;
            addEventHistoryItem("<!> Unable to initiate call");
            setState(STATE_STOPPED_ERROR);
            return false;
        }
    }
    else if (token1.compare("tone") == 0)
    {
        if (token2.size() == 0)
        {
            //cerr << _myNumber <<"\t<!> TONE command requires a string." <<endl;
            addEventHistoryItem("<!> TONE command requires a string.");
            setState(STATE_STOPPED_ERROR);
            exit(1);
        }
        if (!playTones(token2))
        {
            //cerr << _myNumber <<"\t<!> Unable to play tone '" << token2 << endl;
            addEventHistoryItem("<!> Unable to play tone");
            setState(STATE_STOPPED_ERROR);
            exit(1);
        }
    }
    else if (token1.compare("file") == 0)
    {
        if (token2.size() == 0)
        {
            //cerr << _myNumber <<"\t<!> FILE command requires a file name."<<endl;
            addEventHistoryItem("<!> FILE command requires a file name.");
            setState(STATE_STOPPED_ERROR);
            exit(1);
        }
        if (!playFile(token2))
        {
            //cerr << _myNumber <<"\t<!> Unable to play file '" << token2 << endl;
            addEventHistoryItem("<!> Unable to play file");
            setState(STATE_STOPPED_ERROR);
            exit(1);
        }
    }
    else if (token1.compare("pause") == 0)
    {
        if (token2.size() == 0)
        {
            //cerr << _myNumber <<"\t<!> PAUSE command requires a wait duration."<<endl;
            addEventHistoryItem("<!> PAUSE command requires a wait duration");
            setState(STATE_STOPPED_ERROR);
            exit(1);
        }
        else
        {
            int time = atoi(token2.c_str());
            SLEEP(time);
            if (_playingFile) { 
                sipxCallPlayFileStop(_sipXCall);
                _playingFile=false;
            }
        }
    }
    else if (token1.compare("hang") == 0)
    {
        if (!shutdownCall())
        {
            //cerr << _myNumber <<"\t<!> Unable to shutdown call" <<endl;
            addEventHistoryItem("<!> Unable to shutdown call");
            setState(STATE_STOPPED_ERROR);
            exit(1);
        }
    }
    else
    {
        //cerr << _myNumber <<"\t<!> Unknown command: '" << token1 << "'"<< endl;
        addEventHistoryItem("<!> Unknown command ");
        setState(STATE_STOPPED_ERROR);
        exit(1);
    }

    return true;
}

void SingleCall::tempLockInitializeResources()
{
    setState(STATE_INITIALIZING);
    #ifdef USE_LOADGEN_GUI
    _mutex.lock();
    #else
    boost::mutex::scoped_lock scoped_lock(_mutex); 
    #endif // USE_LOADGEN_GUI
    SIPX_RESULT result = sipxInitialize(&_sipXInst, SIPX_PORT_AUTO, // do NOT change the port settings
                                         SIPX_PORT_AUTO, PORT_NONE, // otherwise he starts working in TCP
                                       currentRtpPort);

    if ( result != SIPX_RESULT_SUCCESS ) {
        //cerr << "ERROR while Itializing: " << result << endl;
        addEventHistoryItem( "<!>ERROR while Initializing" );
        setState(STATE_STOPPED_ERROR);
        exit(1);
    }
    pt2Object[_myNumber] = this;
    sipxConfigEnableRport(_sipXInst, true);
    sipxEventListenerAdd(_sipXInst, wrapper2EventCallBack,  &_myNumber) ;
    #ifdef USE_LOADGEN_GUI
    _mutex.unlock();
    #endif // USE_LOADGEN_GUI
}

void SingleCall::tempLockUnInitializeResources()
{
    setState(STATE_UNINITIALIZING);
    #ifdef USE_LOADGEN_GUI
    _mutex.lock();
    #else
    boost::mutex::scoped_lock scoped_lock(_mutex); 
    #endif // USE_LOADGEN_GUI
    SIPX_RESULT result = sipxUnInitialize(_sipXInst);
    if ( result != SIPX_RESULT_SUCCESS ) {
        addEventHistoryItem( "<!>ERROR while UnInitializing" );
        //cerr << "ERROR while UnItializing" << endl;
    }
    #ifdef USE_LOADGEN_GUI
    _mutex.unlock();
    #endif // USE_LOADGEN_GUI
}

void SingleCall::playAudio()
{
    cout << _myNumber << " Playing audio" << endl;
    sipxAudioEnableSpeaker (_sipXInst, SPEAKER);
}

LOADGEN_STATE SingleCall::getState()
{
    return _state;
}
void SingleCall::setState(const LOADGEN_STATE state)
{
    _state = state;
    #ifdef USE_LOADGEN_GUI
    emit( updateThreadInfo( _myNumber, 
                            _currentRepeat ,
                            QString(SingleCall::humanTranslateLoadgenState(_state).c_str()) 
                          )
        );
    #endif // USE_LOADGEN_GUI
}
int SingleCall::getCurrentRepeat()
{
    return _currentRepeat;
}
string SingleCall::getFilename()
{
    return _filename;
}
int SingleCall::getRepeatCount()
{
    return _repeatCount;
}

int SingleCall::getNumberOfStartedCalls()
{
        return _numberOfStartedCalls;
}

int SingleCall::getNumberOfBadEndedCalls()
{
        return badEndedCalls;
}

int SingleCall::getPercentageGoodCalls()
{
        return 100-((100*badEndedCalls)/_numberOfStartedCalls);
}

void SingleCall::incrementBadEndedCalls()
{
    SingleCall::badEndedCalls++;
    #ifdef USE_LOADGEN_GUI
    emit( updateStatistics() );
    #endif // USE_LOADGEN_GUI
}
string SingleCall::humanTranslateLoadgenState(LOADGEN_STATE state)
{
    switch (state) 
    {
        case STATE_LOADED:              return "loaded ";
            break;
        case STATE_RUNNING:             return "running ";
            break;
        case STATE_INITIALIZING:        return "initializing ";
            break;
        case STATE_CALLING:             return "calling ";
            break;
        case STATE_HANGED_UP:           return "hanged-up ";
            break;
        case STATE_FORCED_HANGED_UP:    return "forced h-up ";
            break;
        case STATE_UNINITIALIZING:      return "uninitializing ";
            break;
        case STATE_STOPPED:             return "stopped ";
            break;
        case STATE_STOPPED_ERROR:       return "error   ";
        default: return "unknown";
    }

}
#if !defined(_WIN32)
// Dummy definition of JNI_LightButton() to prevent the reference in
// sipXcallLib from producing an error.
void JNI_LightButton(long){}
#endif /* !defined(_WIN32) */

